﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LockCrypt.Core.AutoType {
    /// <summary>
    /// A collection of commands to send to a target Window.
    /// </summary>
    public class AutoTypeConfiguration {
        /// <summary>
        /// Gets or sets the name of the target window.
        /// </summary>
        /// <value>The name of the target window.</value>
        public string TargetWindow { get; set; }

        /// <summary>
        /// Gets or sets the commands to send.
        /// </summary>
        /// <value>The commands to send.</value>
        public List<AutoTypeCommand> Commands { get; set; }

        /// <summary>
        /// Gets the commands in this configuration as a string.
        /// </summary>
        /// <value>The command string.</value>
        public string CommandString {
            get { return Commands.Aggregate("", (commands, command) => commands + command.ToString()); }
        }

            /// <summary>
        /// Initializes a new blank <see cref="AutoTypeConfiguration"/>.
        /// </summary>
        public AutoTypeConfiguration() {
            TargetWindow = string.Empty;
            Commands = new List<AutoTypeCommand>();
        }

        /// <summary>
        /// Initializes a new <see cref="AutoTypeConfiguration"/> from a serialized string.
        /// </summary>
        /// <param name="configuration">The serialized configuration.</param>
        public AutoTypeConfiguration(string configuration) 
            :this() {
            if(string.IsNullOrEmpty(configuration))
                return;
            Match configMatch = Regex.Match(configuration, @"{""target"":""(?<target>.+?)"",\w*""config"":""(?<config>.*)""}", RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
            if(!configMatch.Success) {
                throw new ArgumentException("Invalid configuration");
            }
            TargetWindow = configMatch.Groups[1].Value;
            ParseCommandString(configMatch.Groups[2].Value);
        }

        /// <summary>
        /// Parses a list of commands from a serialized string.
        /// </summary>
        /// <param name="commandString">The command string to parse.</param>
        public void ParseCommandString(string commandString) {
            Commands.Clear();
            string[] commands = Regex.Split(commandString, @"{(\w+:[{}\w% \^\+\-,~(\\$)]+)}", RegexOptions.CultureInvariant | RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
            foreach(string command in commands) {
                try {
                    string[] commandSections = command.Split(':');
                    switch(commandSections[0].ToLowerInvariant()) { // command type
                        case "keys":
                            Commands.Add(new KeyCommand() { Keys = commandSections[1] });
                            break;
                        case "mouseclick":
                        case "mouseup":
                        case "mousedown":
                            CommandType mouseAction = (CommandType)Enum.Parse(typeof(CommandType), commandSections[0], true);
                            Commands.Add(new MouseCommand(mouseAction, commandSections[1]));
                            break;
                        case "wait":
                            Commands.Add(new WaitCommand(int.Parse(commandSections[1])));
                            break;
                    }
                } catch {
                    throw new LockCryptException(string.Format("Invalid command: {0}", command));
                }
            }
        }

        /// <summary>
        /// Serializes this configuration.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this configuration.
        /// </returns>
        public override string ToString() {
            string commandString = this.CommandString;
            return commandString.Length > 0 ? string.Format(@"{{""target"":""{0}"",""config"":""{1}""}}", TargetWindow, CommandString) : string.Empty;
        }
    }
}
